package com.Memory;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class MemoryManager {
    private static final int MEMORY_SIZE = 128; // 内存总大小（单位：K）
    private static final int OS_SIZE = 4; // 操作系统占用空间大小（单位：K）
    private static final int AVAILABLE_MEMORY_START = OS_SIZE; // 可分配空间的起始地址
    private static int id = 1;

    private static List<Partition> fixedPartitions = new ArrayList<>(); // 固定分区列表
    private static List<Partition> allocatedPartitions = new ArrayList<>(); // 已分配分区列表
    private static List<Partition> unallocatedPartitions = new ArrayList<>(); // 未分配分区列表

    public static void main(String[] args) {
        initializeFixedPartitions(); // 初始化固定分区
        initializeMemory(); //初始化可变分区

        Scanner scanner = new Scanner(System.in);
        boolean isRunning = true;

        while (isRunning) {
            displayMainMenu();
            int choice = scanner.nextInt();

            switch (choice) {
                case 1:
                    manageFixedPartitions(scanner);
                    break;
                case 2:
                    manageVariablePartitions(scanner);
                    break;
                case 3:
                    isRunning = false;
                    break;
                default:
                    System.out.println("无效的选择，请重新输入。");
            }
        }

        scanner.close();
    }

//    可变分区中的选择
    private static void manageVariablePartitions(Scanner scanner) {
        boolean isRunning = true;

        while (isRunning) {
            displayVariablePartitionsMenu();
            int choice = scanner.nextInt();

            switch (choice) {
                case 1:
                    allocateVariablePartition(scanner);
                    break;
                case 2:
                    deallocateVariablePartition(scanner);
                    break;
                case 3:
                    isRunning = false;
                    break;
                default:
                    System.out.println("无效的选择，请重新输入。");
            }
        }
    }

//    打印可变分区的菜单
    private static void displayVariablePartitionsMenu() {
        System.out.println("-----------------");
        System.out.println("可变分区管理：");
        System.out.println("1. 分配空间");
        System.out.println("2. 释放空间");
        System.out.println("3. 返回主菜单");
    }

//    初始化可变分区的未分配分区
    private static void initializeMemory() {
        // 创建初始的未分配分区
        Partition initialPartition = new Partition(OS_SIZE, MEMORY_SIZE - OS_SIZE, false, 0, 0);
        unallocatedPartitions.add(initialPartition);
    }

//    可变分区分配空间
    private static void allocateVariablePartition(Scanner scanner) {
        System.out.print("请输入作业大小：");
        int jobSize = scanner.nextInt();
        scanner.nextLine(); // 消耗换行符

        // 在未分配分区中查找合适的分区
        Partition selectedPartition = findVariablePartition(jobSize);

        if (selectedPartition != null) {
            // 更新分区信息
            selectedPartition.setAllocated(true);
            selectedPartition.setJobSize(jobSize);

            // 将已分配分区添加到已分配分区列表
            allocatedPartitions.add(selectedPartition);

            // 输出内存状况
            System.out.println("分配成功！");
            displayMemoryStatus();
        } else {
            System.out.println("没有足够的空间来分配作业。");
        }
    }

//    可变分区根据进程的大小动态的建立分区
    private static Partition findVariablePartition(int jobSize) {
        // 在未分配分区中查找合适的分区
        for (Partition partition : unallocatedPartitions) {
            if (partition.getSize() > jobSize) {
                // 分割出合适的分区
                int newStartAddress = partition.getStartAddress() + jobSize;
                int newSize = partition.getSize() - jobSize;

                // 创建新分区
                Partition newPartition = new Partition(partition.getStartAddress(), jobSize, false, 0,id);
                id = id+1;

                // 更新原分区信息
                partition.setSize(newSize);
                partition.setJobSize(0);
                partition.setStartAddress(newStartAddress);

                return newPartition;
            } else if (partition.getSize() == jobSize) {
                // 分区大小正好等于作业大小
                return partition;
            }
        }
        return null;
    }

//    可变分区的释放
    private static void deallocateVariablePartition(Scanner scanner){
        System.out.print("请输入要释放的分区名称：");
        int partitionName = scanner.nextInt();

        // 在已分配分区中查找要释放的分区
        int partitionID = -1;
        for (Partition partition : allocatedPartitions) {
            if (partition.getName()==partitionName) {
                partitionID = allocatedPartitions.indexOf(partition);
            }
        }

        if (partitionID != -1) {
            // 更新分区信息
            allocatedPartitions.get(partitionID).setAllocated(false);
            allocatedPartitions.get(partitionID).setJobSize(0);

            // 添加释放的分区到未分配分区列表
            unallocatedPartitions.add(allocatedPartitions.get(partitionID));

            // 将已分配分区从已分配分区列表中移除
            allocatedPartitions.remove(partitionID);

            // 合并相邻的未分配分区
            mergeAdjacentPartitions();

            // 输出内存状况
            System.out.println("释放成功！");
            displayMemoryStatus();
        } else {
            System.out.println("未找到要释放的分区。");
        }
    }

//    合并可变分区的相邻的未分配分区
    private static void mergeAdjacentPartitions() {
        // 遍历未分配分区列表，合并相邻的未分配分区
        for (int i = 0; i < unallocatedPartitions.size() - 1 && i>=0; i++) {
            Partition currentPartition = unallocatedPartitions.get(i);
            Partition nextPartition = unallocatedPartitions.get(i + 1);

            // 如果当前分区和下一个分区是相邻的，则合并它们
            if (nextPartition.getStartAddress() + nextPartition.getSize() == currentPartition.getStartAddress()) {
                // 更新当前分区的大小
                currentPartition.setSize(currentPartition.getSize() + nextPartition.getSize());
                currentPartition.setStartAddress(nextPartition.getStartAddress());

                // 从未分配分区列表中移除下一个分区
                unallocatedPartitions.remove(i + 1);

                // 因为移除了一个分区，需要减小索引以继续遍历
                i--;
            }
        }
    }

//    打印可变分区的信息
    private static void displayMemoryStatus() {
        System.out.println("------ 内存分配情况 ------");
        System.out.println("已分配分区：");
        for (Partition partition : allocatedPartitions) {
            System.out.println("分区名字："+partition.getName()+" 起始地址："+partition.getStartAddress()+"k 空间大小："+partition.getSize()+"k 作业大小："+partition.jobSize+"k");
        }

        System.out.println("未分配分区：");
        for (Partition partition : unallocatedPartitions) {
            System.out.println("分区名字："+partition.getName()+" 起始地址："+partition.getStartAddress()+"k 空间大小："+partition.getSize()+"k 作业大小："+partition.jobSize+"k");
        }
    }

//    固定分区中的选择
    private static void manageFixedPartitions(Scanner scanner) {
        boolean isRunning = true;

        while (isRunning) {
            displayFixedPartitionsMenu();
            int choice = scanner.nextInt();

            switch (choice) {
                case 1:
//                    分配空间
                    allocateFixedPartition(scanner);
                    break;
                case 2:
//                    释放空间
                    deallocateFixedPartition(scanner);
                    break;
                case 3:
                    isRunning = false;
                    break;
                default:
                    System.out.println("无效的选择，请重新输入。");
            }
        }
    }

//    打印固定分区菜单
    private static void displayFixedPartitionsMenu() {
        System.out.println("-----------------");
        System.out.println("固定分区管理：");
        System.out.println("1. 分配空间");
        System.out.println("2. 释放空间");
        System.out.println("3. 返回主菜单");
    }

//    固定分区分配空间
    private static void allocateFixedPartition(Scanner scanner) {
        System.out.print("请输入作业大小（单位：K）：");
        int jobSize = scanner.nextInt();

        boolean isAllocated = false;

        for (Partition partition : fixedPartitions) {
            if (!partition.isAllocated() && partition.getSize() >= jobSize) {
                partition.setAllocated(true);
                partition.setJobSize(jobSize);
                isAllocated = true;
                break;
            }
        }

        if (isAllocated) {
            System.out.println("作业分配成功！");
        } else {
            System.out.println("没有足够的空闲分区来分配作业。");
        }

        displayFixedPartitions();
    }

//    释放固定分区中的空间
    private static void deallocateFixedPartition(Scanner scanner) {
        System.out.print("请输入要释放的分区号：");
        int partitionNumber = scanner.nextInt();

        if (partitionNumber >= 0 && partitionNumber < fixedPartitions.size()) {
            Partition partition = fixedPartitions.get(partitionNumber);
            if (partition.isAllocated()) {
                partition.setAllocated(false);
                partition.setJobSize(0);
                System.out.println("分区释放成功！");
            } else {
                System.out.println("该分区没有被分配作业。");
            }
        } else {
            System.out.println("无效的分区号。");
        }

        displayFixedPartitions();
    }

//    打印固定分区状况
    private static void displayFixedPartitions() {
        System.out.println("固定分区情况：");
        System.out.println("分区号\t起始地址\t分区大小\t是否已分配\t作业大小");
        for (int i = 0; i < fixedPartitions.size(); i++) {
            Partition partition = fixedPartitions.get(i);
            System.out.printf("%d\t\t%d\t\t%d\t\t%s\t\t%d\n", i, partition.getStartAddress(), partition.getSize(),
                    partition.isAllocated() ? "是" : "否", partition.getJobSize());
        }
        System.out.println();
    }

//    初始化固定分区列表
    private static void initializeFixedPartitions() {
        fixedPartitions.add(new Partition(0, OS_SIZE, true, 0, 1)); // 操作系统占用分区
        fixedPartitions.add(new Partition(OS_SIZE, 8, false, 0,2)); // 可分配分区1
        fixedPartitions.add(new Partition(OS_SIZE + 8, 16, false, 0,3)); // 可分配分区2
        fixedPartitions.add(new Partition(OS_SIZE + 24, 32, false, 0,4)); // 可分配分区3
    }

//    打印选择分区菜单
    private static void displayMainMenu() {
        System.out.println("------ 主菜单 ------");
        System.out.println("请选择分区管理方式：");
        System.out.println("1. 固定分区");
        System.out.println("2. 可变分区");
        System.out.println("3. 退出");
    }

//    分区单元类
    private static class Partition {
        private int startAddress; // 起始地址
        private int size; // 分区大小
        private boolean isAllocated; // 是否已分配
        private int jobSize; // 作业大小
        private int name; // 分区名称

        public Partition(int startAddress, int size, boolean isAllocated, int jobSize, int name) {
            this.startAddress = startAddress;
            this.size = size;
            this.isAllocated = isAllocated;
            this.jobSize = jobSize;
            this.name = name;
        }

        public int getStartAddress() {
            return startAddress;
        }

        public void setStartAddress(int StartAddress) {
            this.startAddress = StartAddress;
        }

        public int getSize() {
            return size;
        }

        public void setSize(int size) {
            this.size = size;
        }

        public boolean isAllocated() {
            return isAllocated;
        }

        public void setAllocated(boolean allocated) {
            isAllocated = allocated;
        }

        public int getJobSize() {
            return jobSize;
        }

        public void setJobSize(int jobSize) {
            this.jobSize = jobSize;
        }

        public int getName() {
            return name;
        }
    }
}